package com.zekdot.registerclient.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.zekdot.registerclient.dao.EnrollDataDao;
import com.zekdot.registerclient.dao.EnrollMetaDao;
import com.zekdot.registerclient.dto.Envelope;
import com.zekdot.registerclient.entity.EnrollMetaEntity;
import com.zekdot.registerclient.service.EnrollMetaService;
import com.zekdot.registerclient.util.CheckResult;
import com.zekdot.registerclient.util.IdUtil;
import com.zekdot.registerclient.util.JwtUtil;
import io.jsonwebtoken.Claims;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.sql.Timestamp;
import java.util.*;

/**
 * Create by zekdot on 20-3-21.
 */
@Service
public class EnrollMetaServiceImpl implements EnrollMetaService {

    @Resource
    private EnrollMetaDao enrollMetaDao;
    @Resource
    private EnrollDataDao enrollDataDao;
    @Autowired
    private AmqpTemplate rabbitTemplate;
    /**
     * 更新报名元数据状态
     * @param id 报名id
     * @param status 新状态
     */
    private void endEnrollMetaStatus(String id,int status){
        // 首先获得全部没有审核的人
        List<String> uncheckUsers = enrollDataDao.getUncheckedUsername(id);
        // 根据状态构造更新请求列表 第一个位置放报名id，第二个位置放请求列表
        // 列表包括字典 username status rejectReason
        List<Object> list = new LinkedList<>();
        // 第一个位置放报名id
        list.add(id);
        // 存放字典的列表，请求列表
        List<Map<String,Object>> reqs = new LinkedList<>();
        // 构造字典列表
        for(String uncheckUser:uncheckUsers){
            Map<String,Object> req = new HashMap<>();
            req.put("username",uncheckUser);
            // 2代表被拒绝
            req.put("status",2);
            if(status == 1){
                req.put("rejectReason","报名发起者手动停止了请求");
            }else if(status == 2){
                req.put("rejectReason","报名人数已经达到最大值");
            }else if(status == 3){
                req.put("rejectReason","报名已到截止时间");
            }
            reqs.add(req);
        }
        list.add(reqs);
        // 更新元数据状态
        enrollMetaDao.updateStatus(id,status);
        // 提交到消息队列中等待更新
        rabbitTemplate.convertAndSend("UpdateExchange","UpdateRouting",list);
    }

    @Override
    public Envelope newEnroll(EnrollMetaEntity enrollMetaEntity, String username) {
        Date now = new Date();

        enrollMetaEntity.setHost(username);
        enrollMetaEntity.setStartTime(new Timestamp(now.getTime()));
        enrollMetaEntity.setEndTime(null);
        enrollMetaEntity.setStatus(0);
        boolean repeat;
        // 为了防止哈希碰撞，如果碰撞，就重新生成id并进行存储
        do {
            try{
                enrollMetaEntity.setId(IdUtil.getId(username,enrollMetaEntity.getTitle(), String.valueOf(now.getTime())));
                enrollMetaDao.save(enrollMetaEntity);
                repeat = false;
            }catch (DuplicateKeyException e){
                repeat = true;
            }
        }while (repeat);

        Map<String,String> resMap = new HashMap<>();
        // 如果开启了自动审核
        if((enrollMetaEntity.getEnrollLimit() >> 1 & 1) == 1) {
            // 获取用户密码作为令牌生成种子之一
            String password = enrollMetaDao.getPassword(username);
            // 生成操作令牌
            String token = IdUtil.getEnrollToken(enrollMetaEntity.getId(),password);
            // 存储操作令牌
            enrollMetaDao.saveToken(enrollMetaEntity.getId(),token);
            resMap.put("token",token);
        }
        resMap.put("id",enrollMetaEntity.getId());

        return new Envelope(resMap);
    }

    @Override
    public Envelope getEnrollInfo(String id) {
        EnrollMetaEntity enrollMetaEntity = enrollMetaDao.get(id);
        if(enrollMetaEntity == null){
            return new Envelope(1,"您查看的报名信息不存在",null);
        }
        // 检查人数是否已经达标
        if((enrollMetaEntity.getEndMethod() & 1) == 1){
            // 获取当前成功报名人数
            int successNum = enrollDataDao.getEnrollDataCount(id);
            if(successNum >= enrollMetaEntity.getNumLimit()){
                // 停止报名，设置报名状态为人数达到限制终止
                endEnrollMetaStatus(id,2);
//                enrollMetaDao.updateStatus(id,2);
                enrollMetaEntity.setStatus(2);
                return new Envelope(enrollMetaEntity);
            }
        }
        // 检查是否已经达到时间
        if((enrollMetaEntity.getEndMethod() >> 1 & 1) == 1){
            Date now = new Date();
            if(enrollMetaEntity.getPlanEndTime().getTime() <= now.getTime()){
                // 停止报名，设置报名状态为时间达到限制终止
                endEnrollMetaStatus(id,3);
                enrollMetaEntity.setStatus(3);
            }
        }

        // 返回所需信息
        return new Envelope(enrollMetaEntity);
    }

    @Override
    public Envelope updateEnrollStatus(String id,String username, int status) {
        EnrollMetaEntity enrollMetaEntity = enrollMetaDao.get(id);
        if(enrollMetaEntity == null)
            return new Envelope(1,"您查看的报名信息不存在",null);
        if(!enrollMetaEntity.getHost().equals(username))
            return new Envelope(2,"您不能修改别人的报名状态",null);
        if(status != 1)
            return new Envelope(3,"无效的状态参数",null);
        endEnrollMetaStatus(id,1);
        return Envelope.success;
    }

    @Override
    public Envelope getMoreEnrollMeta(String userId, Date lastTime, int length) {
        List<EnrollMetaEntity> list = enrollMetaDao.getMoreEnrollMeta(userId,lastTime,length);
        return new Envelope(list);
    }

    @Override
    public Envelope getEnrollMeta(String eId,String token, String username) {
        Envelope envelope = getEnrollInfo(eId);
        // 如果获取报名信息时出现了问题
        if(envelope.getCode() != 0){
            return envelope;
        }
        EnrollMetaEntity enrollMetaEntity = (EnrollMetaEntity) envelope.getObj();
        // 去除结果提示
        enrollMetaEntity.setStatusTip(null);
        CheckResult checkResult = JwtUtil.validateJWT(token);
        Claims claims = checkResult.getClaims();
        Integer joinMet = (Integer) claims.get("joinMet");
        // 如果要求只能注册用户填写
        if((enrollMetaEntity.getEnrollLimit() & 1) != 1 && joinMet == 0){
            return new Envelope(3,"仅正式用户可以查看与填写该报名",null);
        }
        return new Envelope(enrollMetaEntity);
    }

    @Override
    public Envelope getMetaDesc(String eId, String token, String username) {
        CheckResult checkResult = JwtUtil.validateJWT(token);
        Claims claims = checkResult.getClaims();
        Integer joinMet = (Integer) claims.get("joinMet");
//        System.out.println(joinMet);
//        System.out.println(username);
        Map<String,Object> map = enrollMetaDao.getMetaDesc(username,eId,joinMet);

        if(map == null){
            return new Envelope(1,"不存在该报名信息或者您没有权限查看",null);
        }

        // 检查人数是否已经达标
        if((((int)map.get("endMethod")) & 1) == 1){
            // 获取当前成功报名人数
            int successNum = enrollDataDao.getEnrollDataCount(eId);
            if(successNum >= ((int)map.get("numLimit"))){
                // 停止报名，设置报名状态为人数达到限制终止
                map.put("status",2);
                endEnrollMetaStatus(eId,2);
//                enrollMetaDao.updateStatus(id,2);
//                enrollMetaEntity.setStatus(2);
                return new Envelope(map);
            }
        }
        // 检查是否已经达到时间
        if(((((int)map.get("endMethod")) & 1) >> 1 & 1) == 1){
            Date now = new Date();
            if(((Date)map.get("planEndTime")).getTime() <= now.getTime()){
                // 停止报名，设置报名状态为时间达到限制终止
                endEnrollMetaStatus(eId,3);
                map.put("status",3);
            }
        }

        // 返回所需信息
        return new Envelope(map);
    }

    @Override
    public Envelope getTempToken(String eId) {
        Map<String,Object> claims = new HashMap<>();
        claims.put("eId",eId);
        String token = JwtUtil.createJWT("tempToken","临时令牌",claims,30*60*1000);
        return new Envelope(token);
    }

    @Override
    public Envelope getReuseEnrollData(String eId) {
        // 获取报名元数据信息
        Map<String,Object> map = enrollMetaDao.reuseMetaData(eId);
        // 获取预报名用户
        map.put("users",enrollDataDao.getReuseUser(eId));
        return new Envelope(map);
    }
}
